
package com.haiyou.common.util.object;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Set;

import com.google.common.collect.Sets;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 
 * @Description: class 关系检查
 * @author xingyuan
 * @date 2022年7月8日 上午10:28:25
 */
@Slf4j
public class ClassUtils {

	/**
	 * class 是否在指定的包中
	 * 
	 * @param cls
	 * @param packageName
	 * @return
	 */
	public static boolean inPackage(Class<?> cls, String packageName) {
		return cls.getName().startsWith(packageName);
	}

	/**
	 * 获取class包名
	 * 
	 * @param cls
	 * @return
	 */
	public static String getPackage(Class<?> cls) {
		// return cls.getName().substring(0, cls.getName().lastIndexOf('.'));
		return cls.getPackage().getName();
	}

	/**
	 * 判断该类型是否包含泛型
	 * 
	 * @param type
	 * @return
	 */
	public static boolean isParameterizedType(Type type) {
		return type instanceof ParameterizedType;
	}

	public static String findGetterName(Field field) {
		String name = field.getName();
		String n = name.substring(0, 1).toUpperCase();
		String fix = name.substring(1);
		Class<?> type = field.getType();
		String methodName = null;
		if (type == boolean.class || type == Boolean.class) {
			methodName = "is" + n + fix;
		} else {
			methodName = "get" + n + fix;
		}
		return methodName;
	}

	public static String findSetterName(Field field) {
		String name = field.getName();
		String n = name.substring(0, 1).toUpperCase();
		String fix = name.substring(1);
		return "set" + n + fix;
	}

	public static Method findGetter(Class<?> cls, Field field) {
		Method geter = null;
		String methodName = null;
		try {
			methodName = findGetterName(field);
			geter = cls.getDeclaredMethod(methodName);
			geter.setAccessible(true);
			return geter;
		} catch (NoSuchMethodException e) {
			if (cls == Object.class) {
				log.error(String.format("[%s] can not find getter method[%s]!", field, methodName), e);
				return null;
			}
			cls = ((Class<?>) cls.getSuperclass());
			return findGetter(cls, field);
		}
	}

	public static Method findSetter(Class<?> cls, Field field) {
		Method setter = null;
		String methodName = null;
		Class<?> paramClass = null;
		try {
			methodName = findSetterName(field);
			paramClass = field.getType();
			setter = cls.getDeclaredMethod(methodName, paramClass);
			setter.setAccessible(true);
			return setter;
		} catch (NoSuchMethodException e) {
			if (cls == Object.class) {
				log.error(String.format("[%s] can not find setter method[%s(%s)]!", field, methodName, paramClass), e);
				return null;
			}
			cls = ((Class<?>) cls.getSuperclass());
			return findSetter(cls, field);
		}
	}

	/**
	 * 所有相关的class,自己,父,接口,父类的父类,父类的接口...
	 * 
	 * @param clz
	 * @return
	 */
	public static Set<Class<?>> findAllClasses(Class<?> clz) {
		Set<Class<?>> classes = Sets.newLinkedHashSet();
		searchClasses(clz, classes);
		return classes;
	}

	/**
	 * 所有相关的class,自己,父,接口,父类的父类,父类的接口...
	 * 
	 * @param clz
	 * @param classes 暂存器
	 */
	private static void searchClasses(Class<?> clz, Set<Class<?>> classes) {
		if (classes == null)
			classes = Sets.newLinkedHashSet();
		if (classes.contains(clz))
			return;
		classes.add(clz);
		Class<?> surClz = clz.getSuperclass();
		if (surClz != null) {
			searchClasses(surClz, classes);
		}
		Class<?>[] infs = clz.getInterfaces();
		if (infs != null && infs.length > 0) {
			for (Class<?> inf : infs) {
				searchClasses(inf, classes);
			}
		}
		if (surClz != null) {
			infs = surClz.getInterfaces();
			if (infs != null && infs.length > 0) {
				for (Class<?> inf : infs) {
					searchClasses(inf, classes);
				}
			}
		}
	}

	/**
	 * 检查class的泛型参数类型
	 * 
	 * @param clz 必须是实现者(impl),java的泛型是假的,只能在实现后才能确定泛型
	 * @return
	 */
	public static Class<?>[] getTypeArguments(Class<?> clz) {
		ParameterizedType parameterizedType = (ParameterizedType) clz.getGenericSuperclass();
		Type[] types = parameterizedType.getActualTypeArguments();
		Class<?>[] classes = new Class[types.length];
		for (int i = 0; i < classes.length; i++) {
			if (types[i] instanceof Class)
				classes[i] = (Class<?>) types[i];
		}
		return classes;
	}

	/**
	 * a对象所对应类信息是b对象所对应的类信息的父类或者是父接口，简单理解即a是b的父类或接口</br>
	 * a对象所对应类信息与b对象所对应的类信息相同，简单理解即a和b为同一个类或同一个接口
	 * 
	 * @param a
	 * @param b
	 * @return
	 */
	public static boolean isAssignableFrom(Class<?> a, Class<?> b) {
		if (a == null || b == null)
			return false;
		return a.isAssignableFrom(b);// a对象所对应类信息与b对象所对应的类信息相同，简单理解即a和b为同一个类或同一个接口
	}

	/**
	 * 获取指定的方法
	 * 
	 * @param deep           是否递归向上找父类的方法
	 * @param clz
	 * @param name
	 * @param parameterTypes
	 * @return
	 */
	public static Method findDeclaredMethod(boolean deep, Class<?> clz, String name, Class<?>... parameterTypes) {
		if (clz == null)
			return null;
		Method method = null;
		try {
			method = clz.getDeclaredMethod(name, parameterTypes);
			method.setAccessible(true);
			return method;
		} catch (Exception e) {
			if (clz == Object.class)
				return null;
			if (deep)
				return findDeclaredMethod(deep, clz.getSuperclass(), name, parameterTypes);
			return null;
		}
	}

	/**
	 * 找出所有的方法
	 * 
	 * @param deep      是否递归向上找父类的方法
	 * @param clz
	 * @param methodSet 找到的方法存放在这里
	 */
	public static void findAllDeclaredMethods(boolean deep, Class<?> clz, Set<Method> methodSet) {
		if (clz == null || clz == Object.class)
			return;
		Method[] methods = clz.getDeclaredMethods();
		if (methods != null && methods.length > 0) {
			for (Method method : methods) {
				method.setAccessible(true);
				methodSet.add(method);
			}
		}
		// methods = clz.getMethods();
		// if (methods != null && methods.length > 0) {
		// for (Method method : methods) {
		// method.setAccessible(true);
		// methodSet.add(method);
		// }
		// }
		if (deep)
			findAllDeclaredMethods(deep, clz.getSuperclass(), methodSet);
	}

	public static void findAllDeclaredFields(boolean deep, Class<?> clz, Set<Field> fieldSet) {
		if (clz == null || clz == Object.class)
			return;
		Field[] fields = clz.getDeclaredFields();
		if (fields != null && fields.length > 0) {
			for (Field field : fields) {
				field.setAccessible(true);
				fieldSet.add(field);
			}
		}
		// fields = clz.getFields();
		// if (fields != null && fields.length > 0) {
		// for (Field field : fields) {
		// field.setAccessible(true);
		// fieldSet.add(field);
		// }
		// }
		if (deep)
			findAllDeclaredFields(deep, clz.getSuperclass(), fieldSet);
	}

	public static Field findDeclaredField(boolean deep, Class<?> clz, String name) {
		if (clz == null)
			return null;
		Field field = null;
		try {
			field = clz.getDeclaredField(name);
			field.setAccessible(true);
			return field;
		} catch (Exception e) {
			if (clz == Object.class)
				return null;
			if (deep)
				return findDeclaredField(deep, clz.getSuperclass(), name);
			return null;
		}
	}

}
